﻿using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace XamlMvp.Controls
{
    public class MvpApplication : Application
    {
        /// <summary>
        /// Provides easy access to the root frame of the Phone Application.
        /// </summary>
        /// <returns>The root frame of the Phone Application.</returns>
        public PhoneApplicationFrame RootFrame { get; private set; }

        public MvpApplication()
        {
            InitializeMvpApplication();
        }

        protected virtual PhoneApplicationFrame CreateFrame()
        {
            return new PhoneApplicationFrame();
        }

        void InitializeLifeTimeObjects(IList lifeTimeObjects)
        {
            var phoneApplicationService = new PhoneApplicationService();
            InitializePhoneApplicationService(phoneApplicationService);
            lifeTimeObjects.Add(phoneApplicationService);
        }

        protected virtual void InitializePhoneApplicationService(PhoneApplicationService phoneApplicationService)
        {
            phoneApplicationService.Activated += Application_Activated;
            phoneApplicationService.Launching += Application_Launching;
            phoneApplicationService.Closing += Application_Closing;
            phoneApplicationService.Deactivated += Application_Deactivated;
        }

        protected virtual void StartUp(IPresenterContext context) { }

        protected virtual void DeHydrate(IPresenterContext context) { }

        protected virtual void ReHydrate(IPresenterContext context) { }

        protected virtual void ShutDown(IPresenterContext context) { }

        void Application_Launching(object sender, LaunchingEventArgs e)
        {
            Binders.Presenters.Context = new PresenterContext(PhoneApplicationService.Current.State, false);
            StartUp(Binders.Presenters.Context);
        }

        void Application_Activated(object sender, ActivatedEventArgs e)
        {
            Binders.Presenters.Context = new PresenterContext(PhoneApplicationService.Current.State, true);
            ReHydrate(Binders.Presenters.Context);
        }

        void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
            DeHydrate(new PresenterContext(PhoneApplicationService.Current.State, true));
        }

        void Application_Closing(object sender, ClosingEventArgs e)
        {
            ShutDown(new PresenterContext(PhoneApplicationService.Current.State, false));
        }

        void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
        {
            if (RootVisual != RootFrame)
            {
                RootVisual = RootFrame;
            }
            RootFrame.Navigated -= CompleteInitializePhoneApplication;
        } 

        void InitializeApplication()
        {
            RootFrame = CreateFrame();
            RootFrame.Navigated += CompleteInitializePhoneApplication;
            RootFrame.NavigationFailed += RootFrame_NavigationFailed;

            var uriMapper = new UriMapper();
            AddUriMappings(uriMapper.UriMappings);
            RootFrame.UriMapper = uriMapper;

            Load(new DispatcherWrapper(RootFrame.Dispatcher));
        }

        void InitializeMvpApplication()
        {
            UnhandledException += Application_UnhandledException;

            InitializeApplication();

            if (System.Diagnostics.Debugger.IsAttached)
            {
                InitializeSettings(Application.Current.Host.Settings);
            }

            AddApplicationLifeTimeObjects(ApplicationLifetimeObjects);

            InitializeBinders();

            InitializeHydrators(Binders.Hydrators);

            InitializePresenters(Binders.Presenters);
        }

        protected virtual void InitializeSettings(Settings settings) { }        

        protected virtual void AddApplicationLifeTimeObjects(IList lifeTimeObjects)
        {
            InitializeLifeTimeObjects(lifeTimeObjects);
        }

        protected virtual void AddUriMappings(Collection<UriMapping> mappings)
        {
            mappings.Add(new UriMapping { Uri = new Uri("/{noun}/{verb}", UriKind.Relative), MappedUri = new Uri("/Views/{noun}{verb}View.xaml", UriKind.Relative) });
            mappings.Add(new UriMapping { Uri = new Uri("/{page}", UriKind.Relative), MappedUri = new Uri("/Views/{page}View.xaml", UriKind.Relative) });
        }

        protected virtual void InitializeBinders() {}

        protected virtual void InitializeHydrators(IHydratorBinder hydrators) {}

        protected virtual void InitializePresenters(IPresenterBinder presenters) {}

        protected virtual void Load(IDispatcher dispatcher) {}

        void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            NavigationFailed(e);
        }

        void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            LogUnhandledException(e);   
        }

        protected virtual void NavigationFailed(NavigationFailedEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                System.Diagnostics.Debugger.Break();
            }
        }

        protected virtual void LogUnhandledException(ApplicationUnhandledExceptionEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                System.Diagnostics.Debugger.Break();
            }
        }
    }
}